/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.redis;

import com.esotericsoftware.kryo.Kryo;
import com.iwindplus.base.redis.domain.property.GenericObjectPoolProperty;
import com.iwindplus.base.util.KryoUtil;
import com.iwindplus.base.util.ProtostuffUtil;
import com.iwindplus.base.util.domain.constant.UtilConstant.GenericObjectPoolConstant;
import com.iwindplus.base.util.handler.PooledKryoFactory;
import com.iwindplus.base.util.handler.PooledProtostuffFactory;
import io.protostuff.LinkedBuffer;
import jakarta.annotation.Resource;
import java.time.Duration;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 通用对象池配置.
 *
 * @author zengdegui
 * @since 2025/11/01 02:23
 */
@Slf4j
@Configuration
@EnableConfigurationProperties({GenericObjectPoolProperty.class})
@ConditionalOnProperty(prefix = "generic-object.pool", name = "enabled", havingValue = "true", matchIfMissing = true)
public class GenericObjectPoolConfiguration {

    @Resource
    private GenericObjectPoolProperty property;

    /**
     * 创建 GenericObjectPool<LinkedBuffer>.
     *
     * @return GenericObjectPool<LinkedBuffer>
     */
    @Bean
    public GenericObjectPool<LinkedBuffer> protostuffPool() {
        GenericObjectPoolConfig<LinkedBuffer> cfg = new GenericObjectPoolConfig<>();
        cfg.setMaxTotal(
            Optional.ofNullable(property).map(GenericObjectPoolProperty::getMaxTotal).orElse(GenericObjectPoolConstant.DEFAULT_MAX_TOTAL));
        cfg.setMaxIdle(Optional.ofNullable(property).map(GenericObjectPoolProperty::getMaxIdle).orElse(GenericObjectPoolConstant.DEFAULT_MAX_TOTAL));
        cfg.setMinIdle(Optional.ofNullable(property).map(GenericObjectPoolProperty::getMinIdle)
            .orElse(Math.max(2, GenericObjectPoolConstant.DEFAULT_MAX_TOTAL / 4)));
        cfg.setMaxWait(Optional.ofNullable(property).map(GenericObjectPoolProperty::getMaxWait).orElse(Duration.ofMillis(50)));
        cfg.setTestOnBorrow(Optional.ofNullable(property).map(GenericObjectPoolProperty::getTestOnBorrow).orElse(false));
        cfg.setTestOnReturn(Optional.ofNullable(property).map(GenericObjectPoolProperty::getTestOnReturn).orElse(false));
        cfg.setBlockWhenExhausted(Optional.ofNullable(property).map(GenericObjectPoolProperty::getBlockWhenExhausted).orElse(true));
        cfg.setJmxEnabled(false);
        return new GenericObjectPool<>(new PooledProtostuffFactory(GenericObjectPoolConstant.DEFAULT_BUFFER_SIZE), cfg);
    }

    /**
     * 创建 GenericObjectPool<Kryo>.
     *
     * @return GenericObjectPool<Kryo>
     */
    @Bean
    public GenericObjectPool<Kryo> kryoPool() {
        GenericObjectPoolConfig<Kryo> cfg = new GenericObjectPoolConfig<>();
        cfg.setMaxTotal(
            Optional.ofNullable(property).map(GenericObjectPoolProperty::getMaxTotal).orElse(GenericObjectPoolConstant.DEFAULT_MAX_TOTAL));
        cfg.setMaxIdle(Optional.ofNullable(property).map(GenericObjectPoolProperty::getMaxIdle).orElse(GenericObjectPoolConstant.DEFAULT_MAX_TOTAL));
        cfg.setMinIdle(Optional.ofNullable(property).map(GenericObjectPoolProperty::getMinIdle)
            .orElse(Math.max(2, GenericObjectPoolConstant.DEFAULT_MAX_TOTAL / 4)));
        cfg.setMaxWait(Optional.ofNullable(property).map(GenericObjectPoolProperty::getMaxWait).orElse(Duration.ofMillis(50)));
        cfg.setTestOnBorrow(Optional.ofNullable(property).map(GenericObjectPoolProperty::getTestOnBorrow).orElse(false));
        cfg.setTestOnReturn(Optional.ofNullable(property).map(GenericObjectPoolProperty::getTestOnReturn).orElse(false));
        cfg.setBlockWhenExhausted(Optional.ofNullable(property).map(GenericObjectPoolProperty::getBlockWhenExhausted).orElse(true));
        cfg.setJmxEnabled(false);
        return new GenericObjectPool<>(new PooledKryoFactory(), cfg);
    }

    /**
     * 初始化 ProtostuffUtil.
     *
     * @param pool pool
     * @return Object
     */
    @Bean("protostuffUtilInitializer")
    public Object protostuffUtilInitializer(GenericObjectPool<LinkedBuffer> pool) {
        ProtostuffUtil.setPool(pool);
        log.info("protostuffUtilInitializer");
        return "protostuffUtilInitializer";
    }

    /**
     * 初始化 KryoUtil.
     *
     * @param pool pool
     * @return Object
     */
    @Bean("kryoUtilInitializer")
    public Object kryoUtilInitializer(GenericObjectPool<Kryo> pool) {
        KryoUtil.setPool(pool);
        log.info("kryoUtilInitializer");
        return "kryoUtilInitializer";
    }
}
